#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sched.h>
#include "../Include/channel.h"
#include "../Include/shm_common.h"

#include <errno.h>


void *communicatorFunct(void *args)
{
    queue_t *dQueue=(queue_t *)args;
    pthread_t id=pthread_self(); //tmp
#ifdef COMM_DEBUG
    printf("Communicator thread. Id=%u Proc Id=%d DQueue size=%d\n",id,sched_getcpu(), dQueue->size);
#endif

    struct delegation_t *req;
    //Extract a delegation from the queue. If it is equal to NULL
    //it means that we have to terminate
    while((req=(struct delegation_t*)dequeue(dQueue))!=NULL)
    {
        struct channel_t *ch=req->ch;
        char *msg;
        if(req->buffered && req->to_be_copied)
        {
            //we have to create a copy of the message
            msg=malloc(ch->size);
            memcpy(msg,req->msg,ch->size);

            //signal that the message is modifiable
            spin_lock(&(req->lV));
            notify(&(req->ticket));
            spin_unlock(&(req->lV));
        }
        else //even if it is buffered, we have not to copy it
            msg=req->msg;


	    //printf("[Communicator %d [%d]]: ricevuta delega per send su canale %d\n",req->buffered,sched_getcpu(),ch->id);

        //copy the message to the target variable
        memcpy( MSG(ch,ch->nextVtg), msg, ch->fixedsize - LINE_SIZE );
        int i;
        for( i = ch->fixedsize - LINE_SIZE; i < ch->size; i ++ ) //this is for memcpy inefficiency
        {
            *( MSG(ch,ch->nextVtg)+i ) = *( msg+i );
        }

        //modify (mutual exclusion) buffers' pointers and validity byte
        __LClockChannel( ch );

        //set the validity byte
        *VALB(ch,ch->nextVtg)=true;
        ch->nextVtg = ( ch->nextVtg + 1 ) % ( ch->k + ch->j ); //k=0 for synchr channel

        //wakeup partner (if necessary)
        notify(&(ch->cond_toreceiver));
       //printf("[Communicator %d [%d]]: messaggio copiato e partner svegliato. Messaggi sul canale: %d \n",req->buffered,sched_getcpu(),ch->messages);

        /*
            alternative command: if there is the altKey field setted means that
            someone is polling on the alternative command. Wakeup it...
        */
        /* TODO (dematteis#1#): che se fa con l'alternativo??? */

        /*if( ch->altKey )
        {
            __LCwakeUpAlternative( ch->altKey );
        }*/

        __LCunlockChannel(ch);

        //if it wasn't a buffered send, signal that
        //it has terminated (it is possibile to modify the message)
        if(!req->buffered)
        {
            spin_lock(&(req->lV));
            notify(&(req->ticket));
            spin_unlock(&(req->lV));
        }
        else
        {
            //it was buffered, we can free the message
            free(msg);
        }
    } //cycle

#ifdef COMM_DEBUG
    printf("[Communicator] finalized......\n");
#endif
    return NULL;
}

/*
    This function will create the communicator thread, with the necessary data structures
    and will schedule it on the appropriate thread
*/
struct communicator_t * initCommunicator(int core_mapping[])
{

    int status;
    //create the data structure that will hold the infos about the communicator thread
    struct communicator_t *kp=(struct communicator_t *)malloc(sizeof(struct communicator_t));
    //create the delagation queue
    kp->dQueue=createQueue(DQUEUE_SIZE);
    cpu_set_t mask;

    //set the thread attributes and create the appropriate CPU mask
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

    CPU_ZERO(&mask);
    CPU_SET(core_mapping[sched_getcpu()], &mask); //assign the thread to the correct core

    printf("[initComm] Babbo stava su %d, allora io giro su	%d\n",sched_getcpu(),core_mapping[sched_getcpu()]);


    //create the thread (passing it the reference to the delegation queue) and set the affinity
    status=pthread_create(&(kp->id), &attr, communicatorFunct, (void *)(kp->dQueue));
    if(status) //error: exit
        handle_error(status,"initCommunicator(): unsuccessful creation of the communicator thread");

    status=pthread_setaffinity_np(kp->id,sizeof(cpu_set_t),&mask);
    if(status) //error: exit
        handle_error(status,"initCommunicator(): impossible to set the affinity of the communicator thread");

    return kp;


}

/*
    This function will wait for the termination of the communicator.
    For a gently termination of the communicator thread, we will insert into the
    delegation queue  a special value (a NULL pointer).
    The communicator, when will read it, will terminate its work*/
void finalizeCommunicator(struct communicator_t *kp)
{

    void *status;
    enqueue(kp->dQueue,(void *)(NULL));

    //wait for termination
    pthread_join(kp->id,&status);

    //free the memory
    free(kp->dQueue);
    //free kp
    free(kp);

}
